package it.polimi.se2.agostini_cantini_dignani.meteocal.business;

import com.sun.mail.smtp.SMTPTransport;
import com.sun.net.ssl.internal.ssl.Provider;
import it.polimi.se2.agostini_cantini_dignani.meteocal.entity.Calendar;
import it.polimi.se2.agostini_cantini_dignani.meteocal.entity.Notification;
import it.polimi.se2.agostini_cantini_dignani.meteocal.entity.User;
import it.polimi.se2.agostini_cantini_dignani.meteocal.util.DateTimeUtils;

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.security.Security;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class enables system to send emails using GMail SMTP server. A GMail account from which
 * sending emails needed.
 */
@Asynchronous
@Stateless
public class EmailManager {
	private static final Logger LOGGER = Logger.getLogger(EmailManager.class.getName());
	private static final String USER = "meteocal.acd";
	private static final String PASSWORD = "6ox-d5w-GB6-PBs";
	private static final String URL = "http://localhost:8080/MeteoCal";

	/**
	 * send email using GMail SMTP server.
	 *
	 * @param username       GMail username
	 * @param password       GMail password
	 * @param recipientEmail TO recipient
	 * @param title          title of the message
	 * @param message        message to be sent
	 */
	public void send(final String username, final String password, String recipientEmail, String title, String message) {
		send(username, password, recipientEmail, "", title, message);
	}

	/**
	 * send email using GMail SMTP server.
	 *
	 * @param username       GMail username
	 * @param password       GMail password
	 * @param recipientEmail TO recipient
	 * @param ccEmail        CC recipient. Can be empty if there is no CC recipient
	 * @param title          title of the message
	 * @param message        message to be sent
	 */
	public void send(final String username, final String password, String recipientEmail, String ccEmail, String title, String message) {
		Security.addProvider(new Provider());
		final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

		// Get a Properties object
		Properties props = System.getProperties();
		props.setProperty("mail.smtps.host", "smtp.gmail.com");
		props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
		props.setProperty("mail.smtp.socketFactory.fallback", "false");
		props.setProperty("mail.smtp.port", "465");
		props.setProperty("mail.smtp.socketFactory.port", "465");
		props.setProperty("mail.smtps.auth", "true");

        /*
        If set to false, the QUIT command is sent and the connection is immediately closed. If set 
        to true (the default), causes the transport to wait for the response to the QUIT command.

        ref :   http://java.sun.com/products/javamail/javadocs/com/sun/mail/smtp/package-summary.html
                http://forum.java.sun.com/thread.jspa?threadID=5205249
                smtpsend.java - demo program from javamail
        */
		props.put("mail.smtps.quitwait", "false");

		Session session = Session.getInstance(props, null);

		// -- Create a new message --
		final MimeMessage msg = new MimeMessage(session);

		// -- Set the FROM and TO fields --
		try {
			msg.setFrom(new InternetAddress(username + "@gmail.com"));
		} catch (MessagingException e) {
			LOGGER.log(Level.SEVERE, "Error in the FROM address", e);
		}
		try {
			msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail, false));
		} catch (MessagingException e) {
			LOGGER.log(Level.SEVERE, "Error in the TO address", e);
		}

		if (ccEmail.length() > 0) {
			try {
				msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(ccEmail, false));
			} catch (MessagingException e) {
				LOGGER.log(Level.SEVERE, "Error in the CC address", e);
			}
		}

		try {
			msg.setSubject(title);
			Multipart multipart = new MimeMultipart("alternative");

			MimeBodyPart textPart = new MimeBodyPart();
			String textContent = "";
			textPart.setText(textContent);

			MimeBodyPart htmlPart = new MimeBodyPart();
			htmlPart.setContent(message, "text/html");

			multipart.addBodyPart(textPart);
			multipart.addBodyPart(htmlPart);
			msg.setContent(multipart);
			msg.setSentDate(new Date());
		} catch (MessagingException e) {
			LOGGER.log(Level.SEVERE, "Error creating the message", e);
		}

		try {
			SMTPTransport t = (SMTPTransport) session.getTransport("smtps");

			t.connect("smtp.gmail.com", username, password);
			t.sendMessage(msg, msg.getAllRecipients());
			t.close();
		} catch (MessagingException e) {
			LOGGER.log(Level.SEVERE, "Error sending the message", e);
		}
	}

	public void sendTokenEmail(User u) {
		send(USER, PASSWORD, u.getEmail(), "Meteocal - Confirm registration email", generateTokenBody(u));
	}

	private String generateTokenBody(User u) {
		String htmlBody = generateHeader();
		htmlBody += "<div style='padding:2px'><h3 style='padding:0 0 6px 0;margin:0;font-family:Arial,Sans-serif;font-size:16px;font-weight:bold;color:#222'><span>Welcome to MeteoCal</span></h3> <p>You have successfully signed up. In order to receive notification emails you have to confirm you email address. To do so just go to your Settings and paste the following token: </p> <p style='font-weight: bolder;'>" +
				u.getToken().getTokenString() + "</p></div></td></tr>";
		htmlBody += generateFooter(u.getEmail());
		return htmlBody;
	}

	public void sendNotificationEmail(Notification notification) {
		send(USER, PASSWORD, notification.getOwner().getEmail(), "", generateEmailTitle(notification), generateEmailBody(notification));
	}

	private String generateEmailTitle(Notification notification) {
		final String GENERIC_TITLE = "MeteoCal - Notification";
		final String INVITATION_TITLE = "MeteoCal - Invitation notification";
		final String WEATHER_TITLE = "MeteoCal - Weather notification";

		String title = "";
		switch (notification.getType()) {
			case GENERIC:
				title = GENERIC_TITLE;
				break;
			case INVITATION:
				title = INVITATION_TITLE;
				break;
			case WEATHER:
				title = WEATHER_TITLE;
		}
		return title;
	}

	private String generateEmailBody(Notification notification) {
		String htmlBody = generateHeader();
		htmlBody += "<h3 style='padding:0 0 6px 0;margin:0;font-family:Arial,Sans-serif;font-size:16px;font-weight:bold;color:#222'> <span>" +
				notification.getEvent().getTitle() + "</span></h3> <p>" +
				notification.getDescription() + ".</p> <p>Event details:</p> <table cellpadding='0' cellspacing='0' border='0' summary='Event details'> <tbody> <tr> <td style='padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap' valign='top'> <div><i style='font-style:normal'>When</i></div> </td> <td style='padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222' valign='top'>" +
				formatStartTime(notification) +
				"</td></tr>";
		if (notification.getEvent().getPlace() != null) {
			htmlBody += "<tr><td style='padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap' valign='top'><div><i style='font-style:normal'>Where</i></div></td><td style='padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222' valign='top'>" +
					notification.getEvent().getPlace() + "</td></tr>";
		}

		htmlBody += "<tr><td style='padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap' valign='top'><div><i style='font-style:normal'>Who</i></div></td><td style='padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222' valign='top'><table cellspacing='0' cellpadding='0'><tbody>";
		for (Calendar c : notification.getEvent().getParticipantCalendars()) {
			htmlBody += "<tr><td style='padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222'><span style='font-family:Courier New,monospace'>&bull;</span></td><td style='padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222'><div><div style='margin:0 0 0.3em 0'><span><span>" + c.getOwner().getEmail() + "</span></span></div></div></td></tr>";
		}
		htmlBody += "</tbody></table></td></tr></tbody></table></div></td></tr>" +
				generateFooter(notification.getOwner().getEmail());
		return htmlBody;
	}

	private String generateHeader() {
		return "<div><table cellspacing='0' cellpadding='8' border='0' summary='' style='width:100%;font-family:Arial,Sans-serif;border:1px Solid #ccc;border-width:1px 2px 2px 1px;background-color:#fff'> <tbody> <tr> <td><p><img src='http://imageshack.com/a/img538/9632/45vA7N.png' alt='MeteoCal logo' width='176' height='88'/></p> <div style='padding:2px'>";
	}

	private String generateFooter(String email) {
		return "<tr><td style='background-color:#f6f6f6;color:#888;border-top:1px Solid #ccc;font-family:Arial,Sans-serif;font-size:11px'><p>Sent by <a href='" +
				URL + "' target='_blank'>MeteoCal</a></p><p>You received this email to the account <a href='mailto:" +
				email + "' target='_blank'>" + email + "</a> because you set email notifications on for this account.</p><p>You can modify email notifications setting on <a href='" +
				URL + "/user/settings.xhtml' target='_blank'>" +
				URL + "/user/settings.xhtml</a>.</p></td></tr></tbody></table></div>";
	}

	private String formatStartTime(Notification notification) {
		User user = notification.getOwner();
		return DateTimeUtils.format(notification.getEvent().getStartTime(), user.getDateTimePattern(), user.getCalendar().getTimeZone());
	}
}